#include "LecteurFonction.h"

LecteurFonction::LecteurFonction(LecteurSymbole * ls) : tsf(){
    this->ls=ls;
    nbParam=0;
    arbre=declFonction();

    cout << "Syntaxe de la fonction " <<  nom <<" correcte "<< endl;
    
}
string LecteurFonction::getCpp() {
        string code;
//    if  (typeid(*arbre->getValeur())==typeid(ValeurEntiere))
//            code = "int ";
//    else if  (typeid(*arbre->getValeur())==typeid(ValeurReelle))
//            code = "float ";
//    else if  (typeid(*arbre->getValeur())==typeid(ValeurCaractere))
//            code = "char ";
//    else 
//            code = "string ";
        code = arbre->getValeur()->getDeclCpp();
    
    
    code = code + nom+" (" + (*tsf.getTable()[0]).getValeur()->getDeclCpp() + (*tsf.getTable()[0]).getChaine();

    for (unsigned int i=1;i<nbParam;i++) {
        code = code +"," +(*tsf.getTable()[i]).getValeur()->getDeclCpp() + (*tsf.getTable()[i]).getChaine();
    }
    code = code +")";
     code =  code+ " {\n"+ tsf.addDecl(nbParam) + arbre->getCpp();
     code = code + "}\n"; 
     return code;
    
}
////////////////////////////////////////////////////////////////////////////////
Valeur* LecteurFonction::getValeur(vector<Noeud *> p){
    if (p.size()==nbParam) {
    for (unsigned int i = 0; i<p.size();i++)
    {
        (*tsf.getTable()[i]).setValeur(p[i]->getValeur());
    }
    }
    else {
        cout <<"Erreur nb parametre de la fonction " << this->nom <<" : " <<p.size() <<" au lieu de "<< nbParam <<  endl;
        exit(0);
    }
    return arbre->getValeur();
    
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::declFonction() {
    // <decl_fonction ::= fonction <variable> (<variable>{,<variable>}) debut <seqInst> fin
    Noeud* declf = NULL;
    sauterSymCour("fonction");
    testerSymCour("<VARIABLE>");
    nom = ls->getSymCour().getChaine();
    ls->suivant();
    sauterSymCour("(");

    Noeud * noeud;
    noeud =tsf.chercheAjoute(ls->getSymCour());
                    
                    
    nbParam++;
    ls->suivant();
    while (ls->getSymCour()==",") {
        ls->suivant(); 
        tsf.chercheAjoute(ls->getSymCour());
        nbParam++;
        ls->suivant();
    } 
    sauterSymCour(")");
    sauterSymCour("debut");
    declf=seqInst();
    sauterSymCour("fin");
    return declf;
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; } retour <expression>;

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls->getSymCour()=="<VARIABLE>" || ls->getSymCour()=="si"
                || ls->getSymCour()=="tantque" || ls->getSymCour()=="repeter"
                || ls->getSymCour()=="pour"|| ls->getSymCour()=="lire" 
                || ls->getSymCour()=="ecrire" || ls->getSymCour()=="switch");
	// tant que le symbole courant est un debut possible d'instruction...
        sauterSymCour("retour");
        si->setRetour(expression());
        sauterSymCour(";");
        
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::inst() {
// <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instPour> | <instSwitch>
    
        if (ls->getSymCour()=="<VARIABLE>") {
            tsf.chercheAjoute(ls->getSymCour());
            return affectation();
        }
        else if (ls->getSymCour()=="si") {
            return instSi();
        }
        else if (ls->getSymCour()=="tantque") {
            return instTq();
        }
        else if (ls->getSymCour()=="repeter") {
            return instRepeter();
        }
        else if (ls->getSymCour()=="pour") {
            return instPour();
        }
        else if (ls->getSymCour()=="lire") {
            return instLire();
        }
        else if (ls->getSymCour()=="ecrire") {
            return instEcrire();
        }
        else if (ls->getSymCour()=="switch") {
            return instSwitch();
        }
        else {
            erreur("<inst>");
            return NULL;
        }
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::affectation() {
// <affectation> ::= <variable> = <expression> | <variable>++ | <variable>--

	testerSymCour("<VARIABLE>");
	Noeud* var = tsf.chercheAjoute(ls->getSymCour());
	ls->suivant();
        Noeud* exp;
        if(ls->getSymCour()=="=") // affectation
        {
                ls->suivant();
                exp = expression();
        }
        else if (ls->getSymCour()=="+")  // incrémentation
        {
            ls->suivant();
            sauterSymCour("+");
            exp = new NoeudOperateurBinaire(Symbole("+"), var, tsf.chercheAjoute(Symbole("1")));
        }
        else if (ls->getSymCour()=="-")
        {
            ls->suivant();
            sauterSymCour("-");
            exp = new NoeudOperateurBinaire(Symbole("-"), var, tsf.chercheAjoute(Symbole("1")));
        }
        else
            erreur("Affectation attendue.");
        return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::expression() {
// <expression> ::= <terme> { <opBinaire> <terme> }

	Noeud* term = terme();
	Symbole operateur;
	while (ls->getSymCour()=="+" || ls->getSymCour()=="-") {
		operateur = opAdd(); // on stocke le symbole de l'opérateur
		Noeud* termDroit=terme(); // lecture de l'operande droit
		term = new NoeudOperateurBinaire(operateur,term,termDroit); // const. du noeud
	}
	return term;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::terme()       
    // <terme> ::= <facteur> { <opMult> <facteur>}
    {
        Symbole operateur;
        Noeud * fact= facteur();
        while (ls->getSymCour()=="*" || ls->getSymCour()=="/")
        {
            operateur = opMult();  //on stocke le symbole de l'opérateur
            Noeud* factDroit= facteur(); 
    
            fact = new NoeudOperateurBinaire(operateur,fact,factDroit);
        }
        return fact;
    }
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::facteur() {
// <facteur> ::= <entier> | <reel>  |  <variable> | <opUnaire> <expBool> | ( <expBool> )

	Noeud* fact=NULL;

	if (ls->getSymCour()=="<VARIABLE>" || ls->getSymCour()=="<ENTIER>" || ls->getSymCour()=="<REEL>"
                || ls->getSymCour()=="<CHAINE>" || ls->getSymCour()=="<CARACTERE>") {
		fact=tsf.chercheAjoute(ls->getSymCour());
		ls->suivant();
	} else if (ls->getSymCour()=="-" || ls->getSymCour()=="non") {
		// on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
		fact = new NoeudOperateurBinaire(opUnaire(), tsf.chercheAjoute(Symbole("0")), expBool());
        } else if (ls->getSymCour()=="(") {
		ls->suivant();
		fact=expBool();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}


////////////////////////////////////////////////////////////////////////////////
/*Symbole LecteurFonction::opBinaire() {
// <opBinaire> ::= + | - | *  | /
	Symbole operateur;
	if (ls->getSymCour()=="+" || ls->getSymCour()=="-" || 
		ls->getSymCour()=="*" || ls->getSymCour()=="/" ) {
		operateur=ls->getSymCour();
		ls->suivant();
	}
	else
		erreur("<opBinaire>");
	return operateur;
}*/

/////////////////////////////////////////////////////    
    ///////////////////////////
Symbole LecteurFonction::opAdd()
// <opAdd> ::= + | -
{
    Symbole operateur;
	if (ls->getSymCour()=="+" || ls->getSymCour()=="-")
        {
                operateur = ls->getSymCour();
		ls->suivant();
        }
	else
		erreur("<opAdd>");
    return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurFonction::opMult()
// <opMult> ::= * | /
{
        Symbole operateur;
	if (ls->getSymCour()=="*" || ls->getSymCour()=="/")
        {
                operateur = ls->getSymCour();
		ls->suivant();
        }
	else
		erreur("<opMult>");
        return operateur;
}
 ///////////////////////////////////////////////////////////////////////////////

Symbole LecteurFonction::opRel()
// <opRel> ::= == | != | < | <= | > | >=
{
    Symbole operateur;
    if (ls->getSymCour()=="==" 
             || ls->getSymCour()=="!=" 
             || ls->getSymCour()=="<" 
             || ls->getSymCour()=="<=" 
             || ls->getSymCour()==">" 
             || ls->getSymCour()==">=")
    {
        operateur = ls->getSymCour();
        ls->suivant();
    }
    else
        erreur("<opRel>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurFonction::opUnaire()
// <opUnaire> ::= -|non
{
    Symbole operateur;
    if(ls->getSymCour()=="-" || ls->getSymCour()=="non")
    {
        operateur = ls->getSymCour();
        ls->suivant();
    }
    else
        erreur("<opUnaire>");
    return operateur;
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::expBool()     
// <expBool> ::= <concatenation> { ou <concatenation> }
{
    Symbole operateur;
    Noeud* conc = concatenation();
    while (ls->getSymCour() == "ou")
    {
        operateur = ls->getSymCour();
        ls->suivant();
        Noeud* concDroit =  concatenation();
        
        conc = new NoeudOperateurBinaire(operateur,conc,concDroit);
    }
    return conc;
}
////////////////////////////////////////////////////////////////////////////////
    Noeud* LecteurFonction::concatenation()
    // <concatenation> ::= <relation> { et <relation> }
    {
        Symbole operateur;
        Noeud* rel = relation();
        while (ls->getSymCour() == "et")
        {
            operateur = ls->getSymCour();
            ls->suivant();
            Noeud* relDroit =  concatenation();

            rel = new NoeudOperateurBinaire(operateur,rel,relDroit);
        }
        return rel;
    }
   
////////////////////////////////////////////////////////////////////////////////
    
Noeud* LecteurFonction::relation()   
// <relation> ::= <expression> { <opRel> <expression> }
{
        Symbole operateur;
        Noeud * exp= expression();
        while (ls->getSymCour()=="==" 
             || ls->getSymCour()=="!=" 
             || ls->getSymCour()=="<" 
             || ls->getSymCour()=="<=" 
             || ls->getSymCour()==">" 
             || ls->getSymCour()==">=")
        {
            operateur = opRel();  //on stocke le symbole de l'opérateur
            Noeud* expDroit= expression(); 
    
            exp = new NoeudOperateurBinaire(operateur,exp,expDroit);
        }
        return exp;
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::instSi()      
//<instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi
    {
        sauterSymCour("si");
        sauterSymCour("(");
        Noeud* exp = expBool();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        NoeudInstSi* instSi = new NoeudInstSi(exp, seq, NULL);
        NoeudInstSi* instSinon = instSi;
        while (ls->getSymCour()=="sinonsi") {
            ls->suivant();
            sauterSymCour("(");
            exp = expBool();
            sauterSymCour(")");
            seq = seqInst();
            instSinon = instSinon->setSinon(exp, seq);
        }
        if (ls->getSymCour()=="sinon") {
            ls->suivant();
            seq = seqInst();
            instSinon->setSinon(new NoeudOperateurBinaire(Symbole("0"),NULL,NULL),seq); // renvoie true
        }
        sauterSymCour("finsi");
                
        return instSi;
    }

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::instTq()
{   
    sauterSymCour("tantque");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("fintantque");
    return (new NoeudInstTantque(cond, seq));
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::instRepeter()
{   
    
    sauterSymCour("repeter");
    Noeud* seq=seqInst();
    sauterSymCour("jusqua");
    sauterSymCour("(");
    Noeud* cond = expBool();
    sauterSymCour(")");
    return (new NoeudInstRepeter(cond, seq));
}
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::instPour()
{   
    sauterSymCour("pour");
    sauterSymCour("(");
    Noeud* init = affectation();
    sauterSymCour(";");
    Noeud* cond = expBool();
    sauterSymCour(";");
    Noeud* iter = affectation();
    sauterSymCour(")");
    Noeud* seq = seqInst();
    sauterSymCour("finpour");
    return (new NoeudInstPour(cond, seq,init,iter));
}
////////////////////////////////////////////////////////////////////////////////

    Noeud* LecteurFonction::instLire()    
    // <instLire> ::= lire ( <variable> )
    {
    sauterSymCour("lire");
    sauterSymCour("(");
    testerSymCour("<VARIABLE>");
    SymboleValue* var= tsf.chercheAjoute(ls->getSymCour());
    ls->suivant();
    sauterSymCour(")");
    
    return new NoeudInstLire(var);
    }
    
////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::instEcrire()  
// <instEcrire> ::= ecrire ( <expression> | <chaine> )
{
    Noeud * ecr;
    sauterSymCour("ecrire");
    sauterSymCour("(");
    if (ls->getSymCour()=="<CHAINE>") {
        ecr= tsf.chercheAjoute(ls->getSymCour());
        ls->suivant();
    }
    else if (ls->getSymCour()=="<ENTIER>" || ls->getSymCour()=="<VARIABLE>"
            ) {
        ecr= expression();
    }
    else if (ls->getSymCour()=="-" || ls->getSymCour()=="non" 
            || ls->getSymCour()=="(" ) {
        ecr = expression();
        ls->suivant();
    }
    else {
        erreur("<instEcrire>");
    }
    sauterSymCour(")");
    return new NoeudInstEcrire(ecr);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurFonction::instSwitch()  
// <instSwitch> ::= switch ( <variable> ) {case <valeur>: <seqInst> fincase} [default: <seqInst>]
{
    SymboleValue* cond;
    NoeudInstSwitch* _switch;
    SymboleValue* val;
    Noeud* seq;
    sauterSymCour("switch");
    sauterSymCour("(");
    testerSymCour("<VARIABLE>");
    cond = tsf.chercheAjoute(ls->getSymCour());
    ls->suivant();
    sauterSymCour(")");
    _switch = new NoeudInstSwitch(cond);
    do
    {
        sauterSymCour("case");
        testerSymCour("<ENTIER>");
        val = tsf.chercheAjoute(ls->getSymCour());
        ls->suivant();
        sauterSymCour(":");
        seq = seqInst();
        sauterSymCour("fincase");
        sauterSymCour(";");
        _switch->ajouteCase(new NoeudInstCase(val, seq, false));
    }while(ls->getSymCour()=="case");
    if(ls->getSymCour() == "default")
    {
        ls->suivant();
        sauterSymCour(":");
        seq=seqInst();
        _switch->ajouteCase(new NoeudInstCase(cond, seq, true));
    }
    sauterSymCour("finswitch");
    return _switch;
}
////////////////////////////////////////////////////////////////////////////////
void LecteurFonction::testerSymCour(string ch) {
	if (ls->getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls->getLigne()
				<< " - Colonne " << ls->getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls->getSymCour() << endl
				<< endl;
		exit(0); // plus tard, on levera une exception
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurFonction::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls->suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurFonction::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls->getLigne() << " - Colonne "
			<< ls->getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls->getSymCour() << endl << endl;
	exit(0); // plus tard, on levera une exception
}
